#include "Player.h"
#include "../SGD Wrappers/SGD_GraphicsManager.h"
#include "../SGD Wrappers/SGD_InputManager.h"
#include "../SGD Wrappers/SGD_Event.h"
#include "FireGrappleMessage.h"
#include "GamePlayState.h"
#include "HowToState.h"
#include "DestroyEntityMessage.h"
#include <sstream>
#include "ParticleManager.h"
#include <math.h>
#include "Physics.h"
#include "Game.h"
#include "Enemy.h"
#include "BitmapFont.h"

Player::Player() : Listener(this)
{
	Listener::RegisterForEvent("PARTICLE_DAMAGE");
	Listener::RegisterForEvent("GRAPPLEDETACH");
	healthBar = SGD::GraphicsManager::GetInstance()->LoadTexture("resources/images/HealthBar.png");
	corruptionBar = SGD::GraphicsManager::GetInstance()->LoadTexture("resources/images/HealthBar_corrupt.png");
	image = SGD::GraphicsManager::GetInstance()->LoadTexture("resources/images/warrior.png");
	slackGrapple = SGD::GraphicsManager::GetInstance()->LoadTexture("resources/images/slackrope.png");
}


Player::~Player()
{
	SGD::GraphicsManager::GetInstance()->UnloadTexture(healthBar);
	SGD::GraphicsManager::GetInstance()->UnloadTexture(corruptionBar);
	SGD::GraphicsManager::GetInstance()->UnloadTexture(image);
	SGD::GraphicsManager::GetInstance()->UnloadTexture(slackGrapple);
	Release();
}

void Player::Update(float elapsedTime)
{
	if (grappleDelay > 0.0f)
		grappleDelay -= elapsedTime;
	else
	{
		SGD::InputManager* input = SGD::InputManager::GetInstance();

		SGD::Vector joystickL = input->GetLeftJoystick(0);
		SGD::Vector joystickR = input->GetRightJoystick(0);

		if (!Lose)
		{
			if (jumptimer > 0)
			{
				jumptimer -= elapsedTime;
				if (jumptimer < 0)
					jumptimer = 0;
			}

			if (acheivementTimer > 0)
			{
				acheivementTimer -= elapsedTime;
				if (acheivementTimer <= 0)
				{
					acheivementTimer = 0;
					if (grappleAcheivement)
						grapplesFired = 0;
					if (lightOrbAcheivement)
						lightOrbsCollected = 0;
					if (enemyAcheivement)
						enemiesKilled = 0;
				}
			}

			if (CurrentPlanet != nullptr && GrappledObject == nullptr)
			{
				SGD::Vector pullforce = CurrentPlanet->GetRect().ComputeCenter() - GetRect().ComputeCenter();
				pullforce.Normalize();
				rotation = acosf(pullforce.y);
				if (pullforce.x > 0)
					rotation = -rotation;

				if (input->IsKeyPressed(SGD::Key::Space) == true
					|| input->IsButtonPressed(0, 0))
				{
					Jump(this, CurrentPlanet);
					CurrentPlanet = nullptr;
					jumptimer = .05f;
				}
				else if (input->IsKeyDown(SGD::Key::A) == true
					|| -.5f > joystickL.x)
				{
					OrbitCounterClockwise(this, CurrentPlanet);
				}
				else if (input->IsKeyDown(SGD::Key::D) == true
					|| .5f < joystickL.x)
				{
					OrbitClockwise(this, CurrentPlanet);
				}
				else
				{
					SGD::Vector zero = SGD::Vector{ 0, 0 };
					SetVelocity(zero);
				}
			}
			if (DestinationPlanet != nullptr && CurrentPlanet == nullptr)
			{
				SGD::Point tempposition = GetPosition();
				SGD::Vector pullforce = DestinationPlanet->GetRect().ComputeCenter() - GetRect().ComputeCenter();
				float distance = pullforce.ComputeLength();
				pullforce.Normalize();
				rotation = acosf(pullforce.y);
				if (pullforce.x > 0)
					rotation = -rotation;

				if (input->IsKeyDown(SGD::Key::A) == true
					|| -.5f > joystickL.x)
				{
					orbitspeed -= elapsedTime * 100;
					if (orbitspeed < -300)
						orbitspeed = -300;
					OrbitCounterClockwise(this, DestinationPlanet);
				}
				if (input->IsKeyDown(SGD::Key::D) == true
					|| .5f < joystickL.x)
				{
					orbitspeed += elapsedTime * 100;
					if (orbitspeed > 300)
						orbitspeed = 300;
					OrbitClockwise(this, DestinationPlanet);
				}
				if (input->IsKeyDown(SGD::Key::W) == true
					|| -.5f > joystickL.y)
				{
					pullspeed += elapsedTime * 300;
					if (pullspeed > 500)
						pullspeed = 500;
					SetVelocity(GetVelocity() * .4f + pullforce * pullspeed);
				}
				if (input->IsKeyDown(SGD::Key::S) == true
					|| .5f < joystickL.y)
				{
					pullspeed -= elapsedTime * 300;
					if (pullspeed < -500)
						pullspeed = -500;
					SetVelocity(GetVelocity() * .4f + pullforce * pullspeed);
				}
				if (input->IsKeyPressed(SGD::Key::RButton) == true
					|| input->IsButtonDown(0, 0))
				{
					DestinationPlanet = nullptr;
					grappleactive = false;
				}
				if (distance > 400)
				{
					ZeroForce(this);
					SetVelocity(pullforce * 5);
					pullspeed = 0;
					if (input->IsKeyDown(SGD::Key::W) == true
						|| -.5f > joystickL.y)
					{
						pullspeed += elapsedTime * 300;
						if (pullspeed > 500)
							pullspeed = 500;
						SetVelocity(GetVelocity() * .4f + pullforce * pullspeed);
					}
					if (input->IsKeyDown(SGD::Key::S) == true
						|| .5f < joystickL.y)
					{
						pullspeed -= elapsedTime * 300;
						if (pullspeed < -500)
							pullspeed = -500;
						SetVelocity(GetVelocity() * .4f + pullforce * pullspeed);
					}
					if (input->IsKeyDown(SGD::Key::LButton) == true
						|| input->IsButtonDown(0, 0))
					{
						DestinationPlanet = nullptr;
						grappleactive = false;
					}
					if (distance > 400)
					{
						ZeroForce(this);
						SetVelocity(pullforce * 5);
						pullspeed = 0;
						if (input->IsKeyDown(SGD::Key::W) == true
							|| -.5f > joystickL.y)
						{
							SetVelocity(pullforce * 20);
						}
					}

				}


				if (GrappledObject != nullptr)
				{
					/*
					SGD::Vector pullforce = GrappledObject->GetRect().ComputeCenter() - GetRect().ComputeCenter();
					float distance = pullforce.ComputeLength();
					pullforce.Normalize();

					if ((input->IsKeyDown(SGD::Key::A) == true
					|| -.5f > joystickL.x == true)
					&& input->IsKeyDown(SGD::Key::RButton) == true)
					{
					orbitspeed -= elapsedTime * 300;
					if (orbitspeed < -500)
					orbitspeed = -500;
					OrbitCounterClockwise(GrappledObject, this);
					}
					if (input->IsKeyDown(SGD::Key::D) == true && input->IsKeyDown(SGD::Key::RButton) == true)
					{
					orbitspeed += elapsedTime * 300;
					if (orbitspeed > 500)
					orbitspeed = 500;
					OrbitClockwise(GrappledObject, this);
					}
					if (input->IsKeyDown(SGD::Key::W) == true && input->IsKeyDown(SGD::Key::RButton) == true)
					{
					GrappledObject->SetVelocity(GrappledObject->GetVelocity() - pullforce);
					}


					*/
				}


			}
			if (-.5f > joystickR.x || -.5f > joystickR.y
				|| .5f < joystickR.x || .5f < joystickR.y)
				controllerGrapple = true;

			if ((input->IsKeyPressed(SGD::Key::LButton)
				|| -.5f > joystickR.x || -.5f > joystickR.y
				|| .5f < joystickR.x || .5f < joystickR.y)
				&& !grappleactive && canGrapple == 0.0f)
			{
				if (-.5f > joystickR.x || -.5f > joystickR.y
					|| .5f < joystickR.x || .5f < joystickR.y)
				{
					controllerGrapple = true;
					controllerVector = joystickR;
				}
				FireGrappleMessage* f = new FireGrappleMessage(this);
				if (corruption < health)
					corruption += 5;
				f->QueueMessage();
				f = nullptr;
				grappleactive = true;
				grapplesFired++;
				if (grapplesFired > 25 && !grappleAcheivement)
				{
					grappleAcheivement = true;
					acheivementTimer = 5;
					SGD::Event e = { "ACHEIVEMENT" };
					e.SendEventNow(nullptr);
				}
			}
			if (health <= corruption)
				corruption = health;

			if (25 >= corruption)
				health += 1 * elapsedTime;

			if (100 < health)
				health = 100;
			if (0 > health)
				health = 0;
			if (100 < corruption)
				corruption = 100;
			if (0 > corruption)
				corruption = 0;

			if (corruption == health)
			{
				if (corruption > health)
					corruption = health;
				corruptionTimer += elapsedTime;
				if (corruptionTimer >= 5.0f)
					Lose = true;
			}
		}
		Entity::Update(elapsedTime);

		if (Game::GetInstance()->GetCurrentState() == GamePlayState::GetInstance())
		{
			Win = GamePlayState::GetInstance()->WinCheck();

			if (GamePlayState::GetInstance()->OutOfBounds(this))
				Lose = true;
			if (health <= 0)
				Lose = true;

			if (Lose || Win)
			{
				losetimer -= elapsedTime;
			}
		}

		if (canGrapple > 0.0f)
		{
			canGrapple -= elapsedTime;
			if (canGrapple < 0.0f)
				canGrapple = 0.0f;
		}
	}

}


void Player::Render()
{
	SGD::Rectangle rect = { SGD::Point{ position.x - Game::GetInstance()->GetCameraPos().x,
		position.y - Game::GetInstance()->GetCameraPos().y }, size };		SGD::Point healthBarPos = { 15, 15 };
	SGD::Size healthBarSz = { 255, 32 };
	SGD::Size currHealthSz = { 2.55f * health, 32 };
	SGD::Size currCorrSz = { 3.19f * corruption, 250 };
	SGD::Rectangle healthBarBG = { healthBarPos, healthBarSz };
	SGD::Rectangle currHealth = { { 128, 48 }, currHealthSz };
	SGD::Rectangle currCorruption = { { 88, 2 }, currCorrSz };

	SGD::GraphicsManager::GetInstance()->DrawTextureSection(healthBar, { 15, 15 }, currHealth);
	SGD::GraphicsManager::GetInstance()->DrawTextureSection(corruptionBar, { 0, -30 }, currCorruption, 0.0f, {}, {}, { .85f, .5f });

	SGD::Point renderPos{ (position.x - Game::GetInstance()->GetCameraPos().x) * Game::GetInstance()->GetZoomLevel(),
		(position.y - Game::GetInstance()->GetCameraPos().y) * Game::GetInstance()->GetZoomLevel() };

	SGD::GraphicsManager::GetInstance()->DrawTexture(image, renderPos, rotation, GetSize() / 2, {},
	{ Game::GetInstance()->GetZoomLevel(), Game::GetInstance()->GetZoomLevel() });
	int alpha = 51 * (int)corruptionTimer;
	if (alpha > 225)
		alpha = 225;
	SGD::Rectangle corruptionDeathIminent{ 0.0f, 0.0f, float(Game::GetInstance()->GetScreenWidth()),
		float(Game::GetInstance()->GetScreenHeight()) };
	SGD::GraphicsManager::GetInstance()->DrawRectangle(corruptionDeathIminent, { alpha, 255, 0, 0 });

	if (Lose)
	{
		//std::ostringstream oss;
		//oss << "GameOver";
		GamePlayState::GetInstance()->GetGameFont()->Draw("Game Over", Game::GetInstance()->GetScreenWidth() / 3, Game::GetInstance()->GetScreenHeight() / 3, 3, { 255, 255, 150, 255 });
		//SGD::GraphicsManager::GetInstance()->DrawString(oss.str().c_str(), { rect.right, rect.bottom });
	}
	if (Win)
	{
		//std::ostringstream oss;
		//oss << "You Win!";
		GamePlayState::GetInstance()->GetGameFont()->Draw("You Win!", Game::GetInstance()->GetScreenWidth() / 3, Game::GetInstance()->GetScreenHeight() / 3, 3, { 255, 255, 150, 255 });
		//SGD::GraphicsManager::GetInstance()->DrawString(oss.str().c_str(), { rect.right, rect.bottom });
	}
	if (acheivementTimer > 0)
	{
		SGD::Point renderPos{ (position.x + 200 - Game::GetInstance()->GetCameraPos().x) * Game::GetInstance()->GetZoomLevel(),
			(position.y + 200 - Game::GetInstance()->GetCameraPos().y) * Game::GetInstance()->GetZoomLevel() };
		//SGD::GraphicsManager::GetInstance()->DrawRectangle({ renderPos, SGD::Size{ 200, 100 } }, { 0, 0, 0 });
		if (grapplesFired > 25 && Game::GetInstance()->GetCurrentState() != (IGameState*)HowToState::GetInstance()->GetInstance())
			GamePlayState::GetInstance()->GetGameFont()->Draw("Grappling Fiend", Game::GetInstance()->GetScreenWidth() / 2, Game::GetInstance()->GetScreenHeight() / 4 * 3, 2, {});
		//SGD::GraphicsManager::GetInstance()->DrawString("Grappling Fiend", { renderPos.x, renderPos.y + 50 }, {});
		if (lightOrbsCollected > 10)
			GamePlayState::GetInstance()->GetGameFont()->Draw("Herald of Light", Game::GetInstance()->GetScreenWidth() / 2, Game::GetInstance()->GetScreenHeight() / 4 * 3, 2, {});
		//SGD::GraphicsManager::GetInstance()->DrawString("Herald of Light", { renderPos.x, renderPos.y + 50 }, {});
		if (enemiesKilled > 3)
			GamePlayState::GetInstance()->GetGameFont()->Draw("Out for Blood", Game::GetInstance()->GetScreenWidth() / 2, Game::GetInstance()->GetScreenHeight() / 4 * 3, 2, {});
		//SGD::GraphicsManager::GetInstance()->DrawString("Out for Blood", { renderPos.x, renderPos.y + 50 }, {});
	}
	if (canGrapple > 0.0f)
	{
		SGD::Vector up = { 0, 1 };
		float rot = up.ComputeAngle(grappleRenderVector);
		SGD::Point slackRenderPos{ (GetRect().ComputeCenter().x - Game::GetInstance()->GetCameraPos().x) * Game::GetInstance()->GetZoomLevel(),
			(GetRect().ComputeCenter().y - Game::GetInstance()->GetCameraPos().y) * Game::GetInstance()->GetZoomLevel() };
		float slack = 64.f * canGrapple;
		SGD::Rectangle rend = { 5, 0, 19, slack };
		if (GetVelocity().x < 0)
			rot *= -1;
		SGD::GraphicsManager::GetInstance()->DrawTextureSection(slackGrapple, slackRenderPos, rend, rot);
	}
}

void Player::HandleCollision(const IEntity* other)
{
	if (other->GetType() == ENT_PROJECTILE)
	{
		const Projectiles* p = dynamic_cast<const Projectiles*>(other);
		if (p->GetOwner() != this)
		{
			health -= 5;
			ParticleManager::GetInstance()->CreateEmitter("Blood", this);
			SGD::Event e = { "PLAYERHURT" };
			e.SendEventNow(nullptr);
		}
	}
	else if (other->GetType() == ENT_PLANET && CurrentPlanet == nullptr && jumptimer == 0)
	{
		const Planets* p = dynamic_cast<const Planets*>(other);
		CurrentPlanet = const_cast<Planets *>(p);
		if (CurrentPlanet == DestinationPlanet)
		{
			DestinationPlanet = nullptr;
			grappleactive = false;
		}
		SGD::Event e = { "PLANETCOLLIDE", &velocity };
		e.SendEventNow(nullptr);
		SGD::Vector zero = SGD::Vector{ 0, 0 };
		SetVelocity(zero);
		ZeroForce(this);
		orbitspeed = 0;
		pullspeed = 0;
	}
	else if (other->GetType() == ENT_BOSS && CurrentPlanet == nullptr && jumptimer == 0)
	{
		const Planets* p = dynamic_cast<const Planets*>(other);
		CurrentPlanet = const_cast<Planets *>(p);
		if (CurrentPlanet == DestinationPlanet)
		{
			DestinationPlanet = nullptr;
			grappleactive = false;
		}
		SGD::Event e = { "PLANETCOLLIDE", &velocity };
		e.SendEventNow(nullptr);
		SGD::Vector zero = SGD::Vector{ 0, 0 };
		SetVelocity(zero);
		ZeroForce(this);
		orbitspeed = 0;
		pullspeed = 0;
	}
	else if (other->GetType() == ENT_ENEMY)
	{
		const Enemy * p = dynamic_cast<const Enemy*>(other);
		Enemy * object = const_cast<Enemy *>(p);
		if (object->GetVelocity().ComputeLength() + GetVelocity().ComputeLength() > 500 && object != GrappledObject)
		{
			if (object->GetEType() == shaflak && !object->Flipped())
			{

			}
			else
			{
				if (object->GetEType() == shike && object->Spiked())
				{
					health -= GetVelocity().ComputeLength() / 250;
				}
				DestroyEntityMessage* msg = new DestroyEntityMessage(object);
				msg->QueueMessage();
				msg = nullptr;
				enemiesKilled++;
				if (enemiesKilled > 3 && !enemyAcheivement)
				{
					enemyAcheivement = true;
					acheivementTimer = 5;
					SGD::Event e = { "ACHEIVEMENT" };
					e.SendEventNow(nullptr);
				}
			}
		}
		if (GrappledObject == object)
		{
			GrappledObject = nullptr;
			grappleactive = false;
		}

		SGD::Event e = { "ENEMYCOLLIDE" };
		e.SendEventNow(nullptr);

	}
	if (other->GetType() == ENT_ORB)
	{
		const Entity * p = dynamic_cast<const Entity*>(other);
		Entity * object = const_cast<Entity *>(p);
		object->SetSize({ 0, 0 });
		if (object == GrappledObject)
		{
			GrappledObject = nullptr;
		}
		else
		{
			DestroyEntityMessage* msg = new DestroyEntityMessage(object);
			msg->QueueMessage();
			msg = nullptr;
		}
		lightOrbsCollected++;
		if (lightOrbsCollected > 10 && !lightOrbAcheivement)
		{
			lightOrbAcheivement = true;
			acheivementTimer = 5;
			SGD::Event e = { "ACHEIVEMENT" };
			e.SendEventNow(nullptr);
		}
		corruption -= 15;
		SGD::Event e = { "LIGHTORBCOLLIDE" };
		e.SendEventNow(nullptr);
		corruptionTimer = 0.0f;
	}
}

void Player::HandleEvent(const SGD::Event* pEvent)
{
	if (pEvent->GetEventID() == "PARTICLE_DAMAGE")
	{
		health -= 5;
		ParticleManager::GetInstance()->CreateEmitter("Blood", this);
	}
	else if (pEvent->GetEventID() == "GRAPPLEDETACH" && grappleactive)
	{
		grappleRenderVector = DestinationPlanet->GetRect().ComputeCenter() - GetRect().ComputeCenter();
		grappleactive = false;
		SetDestinationPlanet(nullptr);
		canGrapple = 2.0f;
	}

}